
#pragma once

#include <functional>
#include <memory>
#include "../base/timestamp.h"

// Adapted from google-protobuf stubs/common.h

template<typename To, typename From>
inline To implicit_cast(From const &f)
{
	return f;
}

// When you upcast (that is, cast a pointer from type Foo to type
// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
// always succeed.  When you downcast (that is, cast a pointer from
// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
// how do you know the pointer is really of type SubclassOfFoo?  It
// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,
// when you downcast, you should use this macro.  In debug mode, we
// use dynamic_cast<> to double-check the downcast is legal (we die
// if it's not).  In normal mode, we do the efficient static_cast<>
// instead.  Thus, it's important to test in debug mode to make sure
// the cast is legal!
//    This is the only place in the code we should use dynamic_cast<>.
// In particular, you SHOULDN'T be using dynamic_cast<> in order to
// do RTTI (eg code like this:
//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
// You should design the code some other way not to need this.

template<typename To, typename From>     // use like this: down_cast<T*>(foo);
inline To down_cast(From* f)                     // so we only accept pointers
{
	// Ensures that To is a sub-type of From *.  This test is here only
	// for compile-time type checking, and has no overhead in an
	// optimized build at run-time, as it will be optimized away
	// completely.
	if (false)
	{
		implicit_cast<From*, To>(0);
	}

#if !defined(NDEBUG) && !defined(GOOGLE_PROTOBUF_NO_RTTI)
	assert(f == NULL || dynamic_cast<To>(f) != NULL);  // RTTI: debug mode only!
#endif
	return static_cast<To>(f);
}

template<typename To, typename From>
inline std::shared_ptr<To> down_pointer_cast(const std::shared_ptr<From>& f)
{
	if (false)
	{
		implicit_cast<From*, To*>(0);
	}

#ifndef NDEBUG
	assert(f == NULL || dynamic_cast<To*>(get_pointer(f)) != NULL);
#endif
	return std::static_pointer_cast<To>(f);
}

namespace net
{

	// All client visible callbacks go here.

	class Buffer;
	class TcpConnection;
	typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
	typedef std::function<void()> TimerCallback;
	typedef std::function<void(const TcpConnectionPtr&)> ConnectionCallback;
	typedef std::function<void(const TcpConnectionPtr&)> CloseCallback;
	typedef std::function<void(const TcpConnectionPtr&)> WriteCompleteCallback;
	typedef std::function<void(const TcpConnectionPtr&, size_t)> HighWaterMarkCallback;

	// the data has been read to (buf, len)
	typedef std::function<void(const TcpConnectionPtr&,
		Buffer*,
		Timestamp)> MessageCallback;

	void defaultConnectionCallback(const TcpConnectionPtr& conn);
	void defaultMessageCallback(const TcpConnectionPtr& conn,
		Buffer* buffer,
		Timestamp receiveTime);

}
